using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Firestarter.DB.Schema.Test.BO;
using Firestarter.DB.Schema.Test.BO.TestUtils;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.Testability;
using NUnit.Framework;

namespace Firestarter.DB.Schema.Test.Logic
{
    [TestFixture]
    public class TestMergeDBKey
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            string xmlClassDefs = BOBroker.GetClassDefsXml();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(xmlClassDefs, new DtdLoader()).LoadClassDefs());
            BORegistry.DataAccessor = new DataAccessorInMemory();

            MergeableBORegistry.Instance.Register<DBKey, DBMergeDBKey>();
            MergeableBORegistry.Instance.Register<DBKeyColumn, DBMergeDBKeyColumn>();
        }

        [SetUp]
        public virtual void SetupTest()
        {
        }
        // ReSharper disable InconsistentNaming
        [TestCase("KeyName", true)]
        [TestCase("KeyID", false)]
        [TestCase("TableID", false)]
        [TestCase("Approved", false)]
        [TestCase("PendingChangeType", false)]
        public void Test_MustMergeProp_ShouldBeSetForProp(string propName, bool expectedMustMergeProp)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKey> merger = (DBMergeBO<DBKey>)GetKeyMerger();
            var dbForeignKey = new DBKey();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var boProp = dbForeignKey.Props[propName];
            var returnedMustMergeProp = merger.MustMergeProperty(boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeProp, returnedMustMergeProp, "Prop '" + propName + "' should be MustMerge = '" + expectedMustMergeProp +"'");
        }

        [TestCase("KeyColumns", true)]
        [TestCase("MappedDMUniqueConstraint", false)]
        [TestCase("Table", false)]
        public void Test_MustMergeRelationship_ShouleBeSetForRelationships(string relationshipName, bool expectedMustMergeRel)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBKey> merger = (DBMergeBO<DBKey>)GetKeyMerger();
            var dbForeignKey = new DBKey();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------

            var relationship = dbForeignKey.Relationships[relationshipName];
            var returnedMustMergeProp = merger.MustMergeRelationship(relationship);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeRel, returnedMustMergeProp, "Relationship '" + relationship.RelationshipName + "' should be MustMerge = '" + expectedMustMergeRel + "'");
        }

        [Test]
        public void Test_MatchBusinessObject_WhenKeyNameEqual_ShouldBeTrue()
        {
            //---------------Set up test pack-------------------
            DBKey fromKey = new DBKey
                                      {
                                          KeyName = RandomValueGen.GetRandomString()
                                      };
            DBKey toKey = new DBKey { KeyName = fromKey.KeyName };

            DBMergeBO<DBKey> keyMerger = (DBMergeBO<DBKey>)GetKeyMerger();
            //---------------Assert Precondition----------------
            Assert.AreEqual(fromKey.KeyName, toKey.KeyName);
            Assert.AreEqual(fromKey.ToString(), toKey.ToString());
            //---------------Execute Test ----------------------
            var function = keyMerger.MatchBusinessObject(fromKey);
            var matchRelationship = function(toKey);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchRelationship);
        }

        [Test]
        public void Test_MatchBusinessObject_WhenKeyNameNotEqual_ShouldBeFalse()
        {
            //---------------Set up test pack-------------------
            var fromKey = new DBKey
                            {
                                KeyName = RandomValueGen.GetRandomString()
                            };
            var toKey = new DBKey
                            {
                                KeyName = RandomValueGen.GetRandomString()
                            };

            var keyMerger = (DBMergeBO<DBKey>)GetKeyMerger();
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(fromKey.KeyName, toKey.KeyName);
            Assert.AreNotEqual(fromKey.ToString(), toKey.ToString());
            //---------------Execute Test ----------------------
            var function = keyMerger.MatchBusinessObject(fromKey);
            var matchRelationship = function(toKey);
            //---------------Test Result -----------------------
            Assert.IsFalse(matchRelationship);
        }

        [Test]
        public void Test_MatchBO_WhenTableNameNotEqual_ButKeyNameEqual_ShouldBeTrue()
        {
            //---------------Set up test pack-------------------
            var fromKey = TestUtilsDBKey.GetTestFactory()
                .CreateValidBusinessObject();
            var toKey = TestUtilsDBKey.GetTestFactory()
                .WithValue(foreignKey => foreignKey.KeyName, fromKey.KeyName)
                .CreateValidBusinessObject();

            var keyMerger = (DBMergeBO<DBKey>)GetKeyMerger();
            //---------------Assert Precondition----------------
            Assert.AreEqual(fromKey.KeyName, toKey.KeyName);
            Assert.AreNotEqual(fromKey.Table, toKey.Table);
            Assert.AreNotEqual(fromKey.Table.TableName, toKey.Table.TableName);
      //      Assert.AreEqual(fromKey.ToString(), toKey.ToString());
            //---------------Execute Test ----------------------
            var function = keyMerger.MatchBusinessObject(fromKey);
            var matchRelationship = function(toKey);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchRelationship);
        }


        [Test]
        public void Test_MergeKey_WithAddedKeyColumn_ShouldCreateMergedKeyColumn_AndCreateColumns()
        {
            //---------------Set up test pack-------------------
            var fromKey = CreateKey();
            var toKey = GetKeyMerger().MergeBOFrom(fromKey, null);
            toKey.Table = new DBTable();
            var fromKeyColumn = CreateKeyColumn(fromKey);

            //---------------Assert Precondition---------------
            Assert.AreEqual(1, fromKey.KeyColumns.Count);
            Assert.IsNotNull(fromKeyColumn.Column);
            Assert.IsNotNull(fromKey.Table);
            Assert.AreEqual(0, toKey.KeyColumns.Count);
            Assert.IsNotNull(toKey.Table);
            //---------------Execute Test ----------------------
            var merger = GetKeyMerger();
            merger.MergeBOFrom(fromKey, toKey);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toKey.KeyColumns.Count);
            var createdKeyColumn = toKey.KeyColumns[0];
            Assert.IsNotNull(createdKeyColumn.Column, "Column should have been linked to the pkColumn");
            Assert.AreEqual(fromKeyColumn.ColumnName, createdKeyColumn.ColumnName, "Column should have been linked to the pkColumn");
            Assert.AreEqual(ChangeType.Add, createdKeyColumn.Column.PendingChangeType);
        }

        [Test]

        public void Test_MergeKey_WithAddedKeyColumn_ShouldAddCreatedColumnsToParentAndRelatedTables()

        {
            //---------------Set up test pack-------------------
            var fromKey = CreateKey();
            var toKey = GetKeyMerger().MergeBOFrom(fromKey, null);
            toKey.Table = new DBTable();
            var fromKeyColumn = CreateKeyColumn(fromKey);

            //---------------Assert Precondition---------------
            Assert.AreEqual(1, fromKey.KeyColumns.Count);
            Assert.IsNotNull(fromKeyColumn.Column);
            Assert.IsNotNull(fromKey.Table);

            Assert.AreEqual(0, toKey.KeyColumns.Count);
            Assert.IsNotNull(toKey.Table);
            //---------------Execute Test ----------------------
            DBMergeBO<DBKey> merger = (DBMergeBO<DBKey>)GetKeyMerger();
            merger.MergeBOFrom(fromKey, toKey);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toKey.KeyColumns.Count);
            var createdKeyColumn = toKey.KeyColumns[0];

            Assert.IsTrue(toKey.Table.Columns.Contains(createdKeyColumn.Column), "The Column should have been added to the table");
        }
        // ReSharper restore InconsistentNaming
        private static IMergeBO<DBKey> GetKeyMerger()
        {
            return MergeableBORegistry.Instance.Resolve<DBKey>();
        }

        private static DBKey CreateKey()
        {
            var parentTable = TestUtilsDBTable.GetTestFactory()
                .WithValue(table => table.Database, null)
                .CreateValidBusinessObject();
            return CreateKey(parentTable);
        }

        private static DBKey CreateKey(DBTable parentTable)
        {
            return TestUtilsDBKey.GetTestFactory()
                .WithValue(relationship => relationship.Table, parentTable)
                .CreateValidBusinessObject();
        }

        private static DBKeyColumn CreateKeyColumn(DBKey pk, DBColumn parentColumn)
        {
            return TestUtilsDBKeyColumn.GetTestFactory()
                .WithValue(relProp => relProp.Key, pk)
                .WithValue(relProp => relProp.Column, parentColumn)
                .CreateValidBusinessObject();
        }
        private static DBKeyColumn CreateKeyColumn(DBKey pk)
        {
            return CreateKeyColumn(pk, GetRandomString());
        }

        private static string GetRandomString()
        {
            return RandomValueGen.GetRandomString();
        }

        private static DBKeyColumn CreateKeyColumn(DBKey pk, string parentColumnName)
        {
            var parentColumn = TestUtilsDBColumn.GetTestFactory()
                .WithValue(column => column.ColumnName, parentColumnName)
                .WithValue(column => column.Table, pk.Table)
                .CreateValidBusinessObject();

            return CreateKeyColumn(pk, parentColumn);
        }
    }
}